//! Supervision module for handling actor failures

use std::time::Duration;
use std::sync::Arc;
use std::any::Any;

use crate::actor::{PID, SupervisorDirective, ActorError, DefaultActorContext};

/// Statistics for actor restarts
#[derive(Clone, Debug, Default)]
pub struct RestartStatistics {
    /// Number of failures
    pub failure_count: u32,
    /// Last failure time
    pub last_failure_time: Option<std::time::SystemTime>,
}

impl RestartStatistics {
    /// Create new restart statistics
    pub fn new() -> Self {
        Self {
            failure_count: 0,
            last_failure_time: None,
        }
    }
    
    /// Record a failure
    pub fn record_failure(&mut self) {
        self.failure_count += 1;
        self.last_failure_time = Some(std::time::SystemTime::now());
    }
    
    /// Reset statistics
    pub fn reset(&mut self) {
        self.failure_count = 0;
        self.last_failure_time = None;
    }
}

/// Supervisor trait for handling actor failures
pub trait Supervisor {
    /// Get the supervisor directive to apply
    fn get_directive(&self, reason: &ActorError) -> SupervisorDirective;
    
    /// Handle a failure
    fn handle_failure(
        &self,
        ctx: &mut DefaultActorContext,
        pid: &PID,
        reason: &ActorError,
        message: Option<Box<dyn Any + Send>>,
    ) -> SupervisorDirective {
        self.get_directive(reason)
    }
}

/// One-for-one strategy (restart only the failed actor)
#[derive(Clone)]
pub struct OneForOneStrategy {
    /// Maximum number of retries
    pub max_restarts: usize,
    /// Time window for counting restarts
    pub window: Duration,
    /// Decision function
    pub decision_fn: fn(&ActorError) -> SupervisorDirective,
}

impl OneForOneStrategy {
    /// Create a new OneForOneStrategy
    pub fn new(
        max_restarts: usize, 
        window: Duration, 
        decision_fn: fn(&ActorError) -> SupervisorDirective
    ) -> Self {
        Self {
            max_restarts,
            window,
            decision_fn,
        }
    }
}

impl Supervisor for OneForOneStrategy {
    fn get_directive(&self, reason: &ActorError) -> SupervisorDirective {
        (self.decision_fn)(reason)
    }
}

/// All-for-one strategy (restart all siblings when one fails)
#[derive(Clone)]
pub struct AllForOneStrategy {
    /// Maximum number of retries
    pub max_restarts: usize,
    /// Time window for counting restarts
    pub window: Duration,
    /// Decision function
    pub decision_fn: fn(&ActorError) -> SupervisorDirective,
}

impl AllForOneStrategy {
    /// Create a new AllForOneStrategy
    pub fn new(
        max_restarts: usize, 
        window: Duration, 
        decision_fn: fn(&ActorError) -> SupervisorDirective
    ) -> Self {
        Self {
            max_restarts,
            window,
            decision_fn,
        }
    }
}

impl Supervisor for AllForOneStrategy {
    fn get_directive(&self, reason: &ActorError) -> SupervisorDirective {
        (self.decision_fn)(reason)
    }
}

/// Exponential backoff strategy (increasing delay between restarts)
#[derive(Clone)]
pub struct ExponentialBackoffStrategy {
    /// Initial backoff duration
    pub initial_backoff: Duration,
    /// Maximum backoff duration
    pub max_backoff: Duration,
    /// Decision function
    pub decision_fn: fn(&ActorError) -> SupervisorDirective,
}

impl ExponentialBackoffStrategy {
    /// Create a new ExponentialBackoffStrategy
    pub fn new(
        initial_backoff: Duration, 
        max_backoff: Duration,
        decision_fn: fn(&ActorError) -> SupervisorDirective
    ) -> Self {
        Self {
            initial_backoff,
            max_backoff,
            decision_fn,
        }
    }
}

impl Supervisor for ExponentialBackoffStrategy {
    fn get_directive(&self, reason: &ActorError) -> SupervisorDirective {
        (self.decision_fn)(reason)
    }
}

/// Create a new OneForOneStrategy with default settings
pub fn one_for_one(
    max_restarts: usize,
    window: Duration,
    decision_fn: fn(&ActorError) -> SupervisorDirective,
) -> OneForOneStrategy {
    OneForOneStrategy::new(max_restarts, window, decision_fn)
}

/// Create a new AllForOneStrategy with default settings
pub fn all_for_one(
    max_restarts: usize,
    window: Duration,
    decision_fn: fn(&ActorError) -> SupervisorDirective,
) -> AllForOneStrategy {
    AllForOneStrategy::new(max_restarts, window, decision_fn)
}

/// Create a new ExponentialBackoffStrategy with default settings
pub fn exponential_backoff(
    initial_backoff: Duration,
    max_backoff: Duration,
    decision_fn: fn(&ActorError) -> SupervisorDirective,
) -> ExponentialBackoffStrategy {
    ExponentialBackoffStrategy::new(initial_backoff, max_backoff, decision_fn)
}

/// Create a default strategy that always restarts
pub fn always_restart() -> OneForOneStrategy {
    one_for_one(10, Duration::from_secs(30), |_| SupervisorDirective::Restart)
}

/// Create a default strategy that always stops
pub fn always_stop() -> OneForOneStrategy {
    one_for_one(10, Duration::from_secs(30), |_| SupervisorDirective::Stop)
}

/// Create a default strategy that always escalates
pub fn always_escalate() -> OneForOneStrategy {
    one_for_one(10, Duration::from_secs(30), |_| SupervisorDirective::Escalate)
} 